//go:build linux
// +build linux

package filecopy

import (
	"fmt"
	"os"
	"syscall"
	"time"
	"unsafe"
)

// CopyWithMmap 通过 mmap 将文件映射到内存再进行复制。
func CopyWithMmap(srcPath, dstPath string) error {
	start := time.Now()

	src, err := os.Open(srcPath)
	if err != nil {
		return fmt.Errorf("open src: %w", err)
	}
	defer src.Close()

	info, err := src.Stat()
	if err != nil {
		return fmt.Errorf("stat src: %w", err)
	}
	size := info.Size()
	if size == 0 {
		dst, err := os.Create(dstPath)
		if err != nil {
			return fmt.Errorf("create empty dst: %w", err)
		}
		return dst.Close()
	}

	if size > int64(^uint(0)>>1) {
		return fmt.Errorf("file too large for mmap copy: %d bytes", size)
	}
	intSize := int(size)

	dst, err := os.OpenFile(dstPath, os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0o644)
	if err != nil {
		return fmt.Errorf("open dst: %w", err)
	}
	defer dst.Close()

	if err := dst.Truncate(size); err != nil {
		return fmt.Errorf("truncate dst: %w", err)
	}

	srcMap, err := syscall.Mmap(int(src.Fd()), 0, intSize, syscall.PROT_READ, syscall.MAP_SHARED)
	if err != nil {
		return fmt.Errorf("mmap src: %w", err)
	}
	defer func() { _ = syscall.Munmap(srcMap) }()

	dstMap, err := syscall.Mmap(int(dst.Fd()), 0, intSize, syscall.PROT_WRITE, syscall.MAP_SHARED)
	if err != nil {
		return fmt.Errorf("mmap dst: %w", err)
	}
	defer func() { _ = syscall.Munmap(dstMap) }()

	copy(dstMap, srcMap)

	if err := msync(dstMap, syscall.MS_SYNC); err != nil {
		return fmt.Errorf("msync dst: %w", err)
	}

	_ = start
	return nil
}

// msync 封装底层 SYS_MSYNC。
func msync(b []byte, flags int) error {
	if len(b) == 0 {
		return nil
	}
	addr := uintptr(unsafe.Pointer(&b[0]))
	length := uintptr(len(b))

	_, _, errno := syscall.Syscall(syscall.SYS_MSYNC, addr, length, uintptr(flags))
	if errno != 0 {
		return errno
	}
	return nil
}
